{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ced088e0-d651-49de-a8c3-f4d1ff7af091",
   "metadata": {},
   "source": [
    "## 提示词模板  Prompt Template"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b4aa32b8-4e99-4585-b820-f5830a288a5a",
   "metadata": {
    "tags": []
   },
   "source": [
    "LangChain 提供了提示词模板，根据变量动态生成提示词。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1e11d8ca-73bd-4a7c-a966-8b915c7e5c9b",
   "metadata": {
    "tags": []
   },
   "source": [
    "### 什么是提示词模板"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "246f7f27-69b9-4a66-92a9-23a0dd2ecddc",
   "metadata": {
    "tags": []
   },
   "source": [
    "LangChain中，提示词由提示词模板（PromptTemplate）这个包装器对象生成。它包含以下3个元素（每个不是必须的）："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e2176192-a7a9-40b2-b3be-9578673f2a20",
   "metadata": {},
   "source": [
    "1. 明确的指令：这些指令可以指导LLM理解用户的需求，并按照特定的方式进行回应\n",
    "2. 少量示例：这些示例可以帮助 LLM 更好地理解任务，并生成更准确的响应\n",
    "3. 用户输入：用户的输入可以直接引导LLM生成特定答案\n",
    "![图片](./img/prompt_template.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40a3d408-e01b-485d-96b7-2bd6c0b0d760",
   "metadata": {},
   "source": [
    "提示词模板提供了 `format` 方法和`format_prompt`方法，输出可以是字符串、消息列表，以及`ChartPromptValue`形式。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cb86b1dd-7458-474c-b85c-751673f5052a",
   "metadata": {},
   "source": [
    "提示词模板会使用 `to_string`方法将提示词转化为一个字符串。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1dfe50fc-dd3d-4cd4-9605-e244cbeb5c5c",
   "metadata": {},
   "source": [
    "而对于需要输入消息列表的聊天模型包装器，提示词模板则会使用`to_messages`方法将提示词转化为一个消息列表。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca097d7b-fff1-462f-b486-c2a8b7ce16cf",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "tags": []
   },
   "source": [
    "### 提示词模板的输入和输出"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "71edc8a1-3cab-48f2-a4d8-566e084925d1",
   "metadata": {},
   "source": [
    "#### 输入"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "66f5f60e-69f4-47ac-9649-aa27aa94f99c",
   "metadata": {},
   "source": [
    "提示词模板的输入数据可以有很多来源，可分为内部数据和外包数据。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ecadac34-f4fc-484a-a0ed-088ff773982d",
   "metadata": {},
   "source": [
    "针对内部数据。举例来说，LangChain内置了自己的提示词，它们被预先定义在源码 `prompt.py` 文件中，使用时直接导入即可，例如可以导入 `API_RESPONSE_PROMPT`，\n",
    "它是引导模型根据 API 响应回答用户问题的提示词："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f36319e2-8f1e-4056-bd97-77aed76c5cfe",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from langchain.chains.api.prompt import API_RESPONSE_PROMPT"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1380decc-18a5-4f7b-a027-38d85d6a8dc0",
   "metadata": {},
   "outputs": [],
   "source": [
    "# API_RESPONSE_PROMPT 在源码中的定义如下：\n",
    "\n",
    "API_URL_PROMPT_TEMPLATE = \"\"\"You are given the below API Documentation:\n",
    "{api_docs}\n",
    "Using this documentation, generate the full API url to call for answering the user question.\n",
    "You should build the API url in order to get a response that is as short as possible, while still getting the necessary information to answer the question. Pay attention to deliberately exclude any unnecessary pieces of data in the API call.\n",
    "\n",
    "Question:{question}\n",
    "API url:\"\"\"\n",
    "\n",
    "API_RESPONSE_PROMPT_TEMPLATE = (\n",
    "    API_URL_PROMPT_TEMPLATE\n",
    "    + \"\"\" {api_url}\n",
    "\n",
    "Here is the response from the API:\n",
    "\n",
    "{api_response}\n",
    "\n",
    "Summarize this response to answer the original question.\n",
    "\n",
    "Summary:\"\"\"\n",
    ")\n",
    "\n",
    "API_RESPONSE_PROMPT = PromptTemplate(\n",
    "    input_variables=[\"api_docs\", \"question\", \"api_url\", \"api_response\"],\n",
    "    template=API_RESPONSE_PROMPT_TEMPLATE,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "651e1cbc-4d47-48e4-b01c-6841cb03b403",
   "metadata": {},
   "source": [
    "导入 PROMPT 后，格式化外部输入变量，将提示词交给模型平台API:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9a62de5a-dcb5-41ef-b3e5-ce90d576984f",
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt = API_RESPONSE_PROMPT.format(api_docs=\"\", question=\"\",api_url=\"\", api_response=\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "74cd6298-4dc2-496a-b1ed-2098ae32b12e",
   "metadata": {},
   "source": [
    "内置的提示词模板可以解决大多数的业务需求，还可以检查数据格式、规划提示词结构、格式化提示词等。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1144e52a-4de4-4d61-a529-b6889bf96a98",
   "metadata": {},
   "source": [
    "外部数据则是开发者自由添加数据，包括用户的输入、用户和模型的历史聊天记录，以及开发者为模型添加的外部知识库数据、程序运行的上下文管理信息等。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e62ec00-74b5-4419-b293-3afedb3186a7",
   "metadata": {},
   "source": [
    "#### 输出"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "44cd3f6b-a8d8-4f91-9e45-d825a03339b7",
   "metadata": {},
   "source": [
    "同模型包装器的分类，提示词模板包装器也分为：`PromptTemplate` 包装器 和 `ChatPromptTemplate` 包装器两类。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "09325af3-ed21-4e62-8ff3-e2068859a9eb",
   "metadata": {},
   "source": [
    "PrompteTemplate 类型包装器输出一个字符串类型的提示词。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "abdf0b61-e78d-468d-964c-f64e526dfea3",
   "metadata": {},
   "source": [
    "ChatPromptTemplate 包装器可以生成一个消息列表格式的提示词，例如它的输出如下："
   ]
  },
  {
   "cell_type": "raw",
   "id": "0c95d717-4566-4c43-9bbd-3580da466c61",
   "metadata": {},
   "source": [
    "[\n",
    "  SystemMessage(\n",
    "    content=(\n",
    "        'You are a helpful assistant that translates English to French.'\n",
    "    ),\n",
    "    additional_kwargs={}\n",
    "  ),\n",
    "  HumanMessage(\n",
    "    content=(\n",
    "        'I love programming.'\n",
    "    ),\n",
    "    additional_kwargs={}\n",
    "  )\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cb9492a7-6548-433a-8ffd-825711100f6d",
   "metadata": {},
   "source": [
    "### 使用提示词模板构造提示词"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9b057a97-b156-4d06-989f-1cb81946681e",
   "metadata": {
    "tags": []
   },
   "source": [
    "#### PromptTemplate 包装器"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b9f1815a-f8f7-46c7-8ada-11619a8b3966",
   "metadata": {},
   "source": [
    "实例化 PromptTemplate 类时，两个关键参数是 `template` 和 `input_variables`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bc020cf6-8eec-4f1b-9829-b6daf51b529a",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain import PromptTemplate\n",
    "template = \"\"\"\n",
    "You are an expert data scientist with an expertise in building deep learning models.\n",
    "Explain the concept of {concept} in a couple of lines\n",
    "\"\"\"\n",
    "\n",
    "### 实例化 PromptTemplate\n",
    "prompt = PromptTemplate(template=template,inpu_variables=[\"concept\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16ede3dc-a7a4-4982-a0c6-f5c79ebbed69",
   "metadata": {},
   "source": [
    "如果不是通过链组件进行调用，PromptTemplate 还提供了一些方法，例如 `format`方法可以将PromptTemplate 包装器的用户输入和模板字符串变量进行绑定，形成一个完整的提示词，如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19dbf7a1-351a-43d3-889a-9d8dae642f6d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 实例化模板的第二种方法(自动识别 variables)\n",
    "prompt = PromptTemplate.from_template(template)\n",
    "\n",
    "# 通过 format 输出格式化内容(输入变量)：\n",
    "final_prompt = prompt.format(concept=\"NLP\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6537cf06-086d-464a-8489-6fedc1ed97b2",
   "metadata": {},
   "source": [
    "同时，Prompt 还可以被链组件调用，也可以调用其它方法，最终实现内部数据和外部数据的整合，后续会讲到。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ded99141-3da5-4e54-a30c-afb5f37f5fa2",
   "metadata": {},
   "source": [
    "#### ChartPromptTemplate 包装器"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6dd30269-e292-4e37-b363-084bcf7897f1",
   "metadata": {},
   "source": [
    "ChartPromptTemplate 提示词是消息列表，支持输出 Message 对象。LangChain 提供了 ChartPromptTemplate、AIMessagePromptTemplate、HumanMessagePromptTemplate、SystemMessagePromptTemplate。无论其多么复杂，步骤都是通用的。例如:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "620d2c3d-b8e3-4738-a8af-a90c41ba8e2b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.prompts import (\n",
    "  ChartPromptTemplate,\n",
    "  PromptTemplate,\n",
    "  SystemMessagePromptTemplate,\n",
    "  HumanMessagePromptTemplate,\n",
    "  AIMessagePromptTemplate\n",
    ")\n",
    "\n",
    "template = \"\"\"\n",
    "You are an expert data scientist with an expertise in building deep learning models.\n",
    "\"\"\"\n",
    "system_message_prompt = SystemMessagePromptTemplate.from_prompt(template)\n",
    "human_template = \"Explain the concept of {concept} in a couple of lines\"\n",
    "human_message_prompt = HumanMessagePromptTemplate.from_prompt(human_template)\n",
    "\n",
    "## 将上面两个模板对象，转化为 ChartPromptTemplate 包装器\n",
    "chat_prompt = ChartPromptTemplate.from_messages([system_message_prompt, human_message_prompt])\n",
    "# 打印结果如下"
   ]
  },
  {
   "cell_type": "raw",
   "id": "48441e73-d71d-4866-a2ef-0140c995af04",
   "metadata": {},
   "source": [
    "ChartPromptTemplate(\n",
    "   input_variables=['concept']，\n",
    "   output_parser=None,\n",
    "   partial_variables={},\n",
    "   messages=[\n",
    "       SystemMessagePromptTemplate(\n",
    "           prompt = PrompTemplate(\n",
    "               input_variables=[]，\n",
    "               output_parser=None,\n",
    "               partial_variables={},\n",
    "               template=(\n",
    "                   '\\nYou are an expert data scientist with an expertise in building deep learning models.\\n'\n",
    "               ),\n",
    "               template_format='f-string',\n",
    "               validate_template=True\n",
    "           ),\n",
    "           additional_kwargs={}\n",
    "       ),\n",
    "       \n",
    "       HumanMessagePromptTemplate(\n",
    "           prompt = PrompTemplate(\n",
    "               input_variables=['concept']，\n",
    "               output_parser=None,\n",
    "               partial_variables={},\n",
    "               template=(\n",
    "                   'Explain the concept of {concept} in a couple of lines'\n",
    "               ),\n",
    "               template_format='f-string',\n",
    "               validate_template=True\n",
    "           ),\n",
    "           additional_kwargs={}\n",
    "       )\n",
    "   ]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "258801e4-5c4e-4018-8bc0-32bfae769c16",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用 format 方法将用户输入传入包装器，组合成完整的 提示词：\n",
    "chat_prompt.format_prompt(concept=\"NLP\")\n",
    "# 调用 format_prompt 方法获得的是 ChatPromptValue 对象："
   ]
  },
  {
   "cell_type": "raw",
   "id": "3ffa8766-8282-4996-893f-2f3ed951f316",
   "metadata": {},
   "source": [
    "ChatPromptValue(messages=[SystemMessage(content=......,additional_kwargs={}), HumanMessage(content=......,additional_kwargs={}, example=False)])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "83b02f18-ab7f-4bcf-be81-8890e6a1779e",
   "metadata": {
    "tags": []
   },
   "source": [
    "ChatPromptValue 有 `to_string` 和 `to_messages`方法。调用 to_messages 方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "101cacda-5389-4d05-8a03-8df0d11b9a5d",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "chat_prompt.format_prompt(concept=\"NLP\").to_messages() \n",
    "# 结果如下"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0090273d-f1df-4848-9686-b17e41d4e4f5",
   "metadata": {},
   "outputs": [],
   "source": [
    "[SystemMessage(content=.....,additional_kwargs={}),HumanMessage(content=......,additional_kwargs={}, example=False)]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c23d0974-7480-4764-b2b7-7a83a5088126",
   "metadata": {},
   "source": [
    "调用 to_string 方法，结果如下："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b52251a8-9b8c-48ec-aef0-719869da90f6",
   "metadata": {},
   "source": [
    "'System: \\n You are an expert data scientist with an expertise in building deep learning models.\\n\\n Human:Explain the concept of NLP in a couple of lines'"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
